package defaulter

import (
	"net/http"
	"net/url"

	"gitee.com/haodreams/golib/autoroute/controller"
	"gitee.com/haodreams/golib/autoroute/minauth"
	"github.com/gin-gonic/gin"
)

// LoginController 授权相关的操作
type LoginController struct {
	controller.Controller
}

/**
 * @description: 安全认证扫描
 * @param {*gin.Context} ctx
 * @return {*}
 */
func SecureHeader(ctx *gin.Context) {
	// 解决安全漏洞：检测到目标服务器启用了OPTIONS方法
	header := ctx.Writer.Header()
	header.Set("Access-Control-Allow-Origin", "*")
	// Access-Control-Allow-Credentials跨域问题
	header.Set("Access-Control-Allow-Credentials", "true")
	header.Set("Access-Control-Allow-Methods", "GET, HEAD, POST, PUT, DELETE")
	header.Set("Access-Control-Max-Age", "86400")
	header.Set("Access-Control-Allow-Headers", "*")

	// 点击劫持：X-Frame-Options未配置
	header.Add("X-Frame-Options", "SAMEORIGIN")
	// 检测到目标Referrer-Policy响应头缺失
	header.Add("Referer-Policy", "origin")
	// 检测到目标Referrer-Policy响应头缺失
	header.Add("Referrer-Policy", "origin")
	// Content-Security-Policy响应头确实
	header.Add("Content-Security-Policy", "object-src 'self'")
	// 检测到目标X-Permitted-Cross-Domain-Policies响应头缺失
	header.Add("X-Permitted-Cross-Domain-Policies", "master-only")
	// 检测到目标X-Content-Type-Options响应头缺失
	header.Add("X-Content-Type-Options", "nosniff")
	// 检测到目标X-XSS-Protection响应头缺失
	header.Add("X-XSS-Protection", "1; mode=block")
	// 检测到目标X-Download-Options响应头缺失
	header.Add("X-Download-Options", "noopen")
	// 点击劫持：X-Frame-Options未配置
	header.Add("X-Frame-Options", "SAMEORIGIN")
	// HTTP Strict-Transport-Security缺失
	header.Add("Strict-Transport-Security", "max-age=63072000; includeSubdomains; preload")
}

/**
 * @description: 开启默认的认证
 * @param {*} cb 扩展的认证方式
 * @return {*}
 */
func UseDefaultAuth(defaultUser func() (user, passwd string), cb ...func(*gin.Context) (interface{}, error)) {
	if len(cb) > 0 && cb != nil {
		controller.SetNeedAuthCallback(cb[0])
	} else {
		controller.SetNeedAuthCallback(minauth.CallbackNeedAuth)
	}
	initUser(defaultUser)
}

// UseHeaderAuth
// func UseHeaderAuth(defaultUser func() (user, passwd string), cb ...func(*gin.Context) (interface{}, error)) {
// 	if len(cb) > 0 && cb != nil {
// 		controller.SetNeedAuthCallback(cb[0])
// 	} else {
// 		controller.SetNeedAuthCallback(minauth.CallbackNeedAuth)
// 	}
// 	authUseHeader = true
// 	initUser(defaultUser)
// }

func initUser(defaultUser func() (user, passwd string)) {
	minauth.Init(defaultUser)
}

// Index ... 登录对话框
// 登录方式1
func (m *LoginController) Index() {
	param := m.GetParam()
	if m.IsPost() {
		username := param.GetTrimString("username")
		password := param.GetTrimString("password")

		if username == "" {
			m.Error("无效的用户名")
			return
		}

		if password == "" {
			m.Error("无效的密码")
			return
		}

		user := minauth.GetUsers().GetUser(username)
		if user == nil {
			m.ErrorWithCode("无效的用户名或密码", http.StatusUnauthorized)
			return
		}

		if user.Name == username && user.Pwd == password {
			clientIP := m.ClientIP()
			sess := minauth.Register(username, "", "", clientIP)
			m.AddCookie("uid", sess.SID, 3600*24*360)
			m.Map["data"] = sess.SID
			m.Msg("登录成功")
			return
		}
		m.Error("无效的用户名或密码")
		return
	}
	m.Map["lastURL"] = param.GetTrimString("lastURL")
	m.Map["lastMsg"] = param.GetTrimString("lastMsg")
	m.Display()
}

// Logout ...
func (m *LoginController) Logout() {
	uid, err := m.Cookie("uid")
	if err != nil {
		m.Msg("成功退出")
		//m.Redirect(302, "/go/login/login")
		return
	}

	minauth.RemoveBySID(uid)
	m.AddCookie("uid", "", -1)
	m.Msg("成功退出")
	//m.Redirect(301, "/go/login/login")
}

// Modify 修改密码
func (m *LoginController) Modify() {
	if m.IsPost() {
		param := m.GetParam()
		pwd := param.GetTrimString("pwd")
		newpwd := param.GetTrimString("newpwd")
		username := ""
		var err error

		uid := m.Request.Header.Get("Authorization")

		if uid == "" {
			//step1 : 检查cookie中是否带有参数
			uid, err = m.Cookie("uid")
			if err != nil || uid == "" {
				//step2 : 检查参数中是否带有uid
				vals, _ := url.ParseQuery(m.Request.URL.RawQuery)
				uids := vals["uid"]
				if len(uids) > 0 {
					uid = uids[0]
				} else {
					return
				}
			}
		}

		sess, err := minauth.FindBySID(uid)
		if err != nil || sess == nil {
			m.ErrorWithCode("登录已经过期,请重新登录", http.StatusUnauthorized)
			return
		}
		username = sess.Name

		user := minauth.GetUsers().GetUser(username)
		if user == nil {
			m.ErrorWithCode("登录已经过期,请重新登录", http.StatusUnauthorized)
			return
		}

		if user.Pwd != pwd {
			m.Error("旧密码错误")
			return
		}

		user.Pwd = newpwd
		err = minauth.Save()
		if err != nil {
			user.Pwd = pwd
			m.Msg("密码更新失败")
			return
		}

		m.Msg("密码修改成功")
		return
	}
	m.Display()
}

// Delete 删除用户
func (m *LoginController) Delete() {
}

// Status 登录状态
func (m *LoginController) Status() {
	uid, err := m.Cookie("uid")
	if err != nil {
		m.Error("需要重新登录")
		return
	}

	_, err = minauth.FindBySID(uid)
	if err != nil {
		m.Error("登录已经过期，需要重新登录")
		return
	}
	m.Msg("OK")
}

// @tags 用户管理
// @Summary 用户登录
// @Description 根据用户名和密码生成uid
// @Param username formData string true "用户名"
// @Param password formData string true "密码"
// @Produce plain
// @Success 200 {string} string "ok" "认证成功"
// @Failure 401 {string} string "code：401 需要认证"
// @Failure 412 {string} string "code：412 参数错误"
// @Router /login/login [post]
func (m *LoginController) Login() {
	param := m.GetParam()
	username := param.GetTrimString("username")
	password := param.GetTrimString("password")

	if username == "" {
		m.ErrorWithCode("无效的用户名", http.StatusUnauthorized)
		return
	}

	if password == "" {
		m.ErrorWithCode("无效的密码", http.StatusUnauthorized)
		return
	}

	user := minauth.GetUsers().GetUser(username)
	if user == nil {
		m.ErrorWithCode("无效的用户名或密码", http.StatusUnauthorized)
		return
	}

	if user.Name == username && user.Pwd == password {
		clientIP := m.ClientIP()
		sess := minauth.Register(username, "", "", clientIP)
		m.AddCookie("uid", sess.SID, 3600*24*7)
		m.Map["data"] = sess.SID
		m.Msg("登录成功")
		return
	}
	m.ErrorWithCode("用户名或密码不正确", http.StatusUnauthorized)
}

func (m *LoginController) HashData() {
	param := m.GetParam()
	data := param.GetTrimString("data")
	if data == "" {
		m.Error("无有效数据")
		return
	}
	m.Msg(minauth.Hash(data))
}
